﻿using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

using vJine.Core.IoC;
using vJine.Core.ORM;
using System;

namespace vJine.Core.IO.Json {
    public partial class JsonHelper {
        static MethodInfo parseObject =
            new Exec<Stream, byte[], JsonHelper>(Parse<JsonHelper>).Method.GetGenericMethodDefinition();
        static void Parse<T>(Stream jsonStream, byte[] B, T jsonObject) {

            Parse(jsonStream, B, true, "{");
            Type T_obj = typeof(T);
            char Index = ','; 
            string Key = null, Value = null;
            StringBuilder sbBuffer = new StringBuilder(50);
            while (Index == ',') {
                Key = parse_key(jsonStream, sbBuffer, B);
                Class<T>.Property p_i = Class<T>.GetProperty(Key);
                if (p_i == null) {
                    if(Key == "Items" && Reflect.IsList(T_obj)) {
                        Index = (char)parseList.MakeGenericMethod(Reflect.GetGenericArgs<T>())
                            .Invoke(null, new object[] { jsonStream, B, jsonObject });
                    } else if(Key == "Items" && Reflect.IsDictionary(T_obj)) {
                       Index = (char)parseDict.MakeGenericMethod(Reflect.GetGenericArgs<T>())
                            .Invoke(null, new object[] { jsonStream, B, jsonObject });
                    } else {
                        throw new JsonException("Property Not Found:[{0}]", Key);
                    }
                } else if(p_i.IsXmlIgnore) {
                    throw new JsonException("Property[{0}] Should Be Ignored.", p_i.Name);
                } else if (p_i.IsPrimitive) {
                    Value = parse_value(jsonStream, sbBuffer, B, ref Index, ",}");
                    p_i.Set(jsonObject, Value);
                } else if(p_i.IsObject) {
                    throw new JsonException("[Object] Type Is Not Supported!,[{0}]", p_i.Name);
                } else {
                    object objObj = p_i.Get(jsonObject);
                    objObj = objObj ?? Class.Create(p_i.pType);
                    parseObject.MakeGenericMethod(p_i.pType)
                            .Invoke(null, new object[] { jsonStream, B, objObj });

                    p_i.Set(jsonObject, objObj);

                    Index = Parse(jsonStream, B, true, ",}");
                }
            }
        }

        static MethodInfo parseList =
            new Call<char, Stream, byte[], IList<JsonHelper>>(Parse<JsonHelper>).Method.GetGenericMethodDefinition();
        static char Parse<T>(Stream jsonStream, byte[] B, IList<T> jsonObject) where T : class, new() {
            Parse(jsonStream, B, true, "["); char Index = '\0';
            do {
                T jsonObj = new T();

                Parse<T>(jsonStream, B, jsonObj);
                jsonObject.Add(jsonObj);

                Index = Parse(jsonStream, B, true, ",]");
            } while (Index == ',');

            return Index;
        }

        static MethodInfo parseDict =
            new Call<char, Stream, byte[], IDictionary<string, string>>(Parse<string, string>).Method.GetGenericMethodDefinition();
        static char Parse<K, V>(Stream jsonStream, byte[] B, IDictionary<K, V> jsonObject) {
            Parse(jsonStream, B, true, "["); char Index = '\0';
            StringBuilder sbBuffer = new StringBuilder(50);
            do {
                K Key =
                    Class.Parse<K>(parse_key(jsonStream, sbBuffer, B));

                V jsonObj = Class.Create<V>();

                Parse<V>(jsonStream, B, jsonObj);
                jsonObject.Add(Key, jsonObj);

                Index = Parse(jsonStream, B, true, ",]");
            } while (Index == ',');

            return Index;
        }

        static char Parse(Stream jsonStream, byte[] B, bool Throw, string Expects) {
            char c = '\0';
            do {
                c = Read(jsonStream, B);
            } while (Ignore(c));

            if (Throw && Expects.IndexOf(c) < 0) {
                throw new JsonException("Un Expected Char:[{0}]", c);
            }

            return c;
        }

        static bool Ignore(char c) {
            return " \b\f\n\r\t\uFEFF".IndexOf(c) > -1;
        }

        static string parse_key(Stream jsonStream, StringBuilder kBuffer, byte[] B) {
            char Index = '\0';
            return parse_value(jsonStream, kBuffer, B, ref Index, ":");
        }

        static string parse_value(Stream jsonStream, StringBuilder vBuffer, byte[] B, ref char Index, string Expects) {
            vBuffer.Remove(0, vBuffer.Length);

            Index = '\0'; int cIndex = -1;
            char c = Parse(jsonStream, B, false, "\"");
            cIndex = Expects.IndexOf(c);

            bool HasComma = false;
            if (c == '"') {
                HasComma = true;
            } else if (cIndex >= 0) {
                Index = Expects[cIndex];
                return null;
            } else {
                vBuffer.Append(c);
            }

            if (HasComma) {
                bool IsSlash = false; bool IsClosed = false;
                do {
                    c = Read(jsonStream, B);
                    if (IsClosed) {
                        cIndex = Expects.IndexOf(c);
                        if (cIndex >= 0) {
                            Index = Expects[cIndex];
                            break;
                        }

                        if (!Ignore(c)) {
                            throw new JsonException("Un Expected Char:[{0}]", c);
                        }
                        continue;
                    }

                    if (IsSlash) {
                        switch (c) {
                            case '\"':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('"');
                                break;
                            case '\\':
                                break;
                            case '/':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('/');
                                break;
                            case 'b':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('\b');
                                break;
                            case 'f':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('\f');
                                break;
                            case 'n':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('\n');
                                break;
                            case 'r':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('\r');
                                break;
                            case 't':
                                vBuffer.Remove(vBuffer.Length - 1, 1);
                                vBuffer.Append('\t');
                                break;
                            case 'u':
                                throw new JsonException("Not Implementation:[/u]");
                            default:
                                throw new JsonException("Code Error:/{0}:", c);
                        }
                        IsSlash = false;
                    } else {
                        IsClosed = c == '"';
                        if (IsClosed) {
                            continue;
                        }

                        vBuffer.Append(c);
                        IsSlash = c == '\\';
                    }
                } while (cIndex < 0);
            } else {
                do {
                    c = Read(jsonStream, B);
                    cIndex = Expects.IndexOf(c);
                    if (cIndex >= 0) {
                        Index = Expects[cIndex];
                        break;
                    }

                    vBuffer.Append(c);
                } while (cIndex < 0);
            }

            return vBuffer.ToString();
        }

        static void Read(Stream stream, byte[] B, char To) {
            char c = '\0';
            do {
                c = Read(stream, B);
                if (Ignore(c)) {
                    continue;
                }

                if (c != To) {
                    throw new JsonException("Un Expected Char:{0}", To);
                }
            } while (c != To);
        }

        static byte b80 = 0x80; //1
        static byte bC0 = 0xC0; //2
        static byte bE0 = 0xE0; //3
        static byte bF0 = 0xF0; //4
        static byte bF8 = 0xF8; //5
        static byte bFC = 0xFC; //6
        static char Read(Stream stream, byte[] B) {
            int len = 1;
            Utility.Read(stream, B, 0, 1);
            byte b = B[0];

            if ((b & bFC) == bFC) {
                len = 6;
            } else if ((b & bF8) == bF8) {
                len = 5;
            } else if ((b & bF0) == bF0) {
                len = 4;
            } else if ((b & bE0) == bE0) {
                len = 3;
            } else if ((b & bC0) == bC0) {
                len = 2;
            } else if ((b & b80) == 0x00) {
                return (char)b;
            } else {
                throw new JsonException("UTF-8 Code Error:[{0:X}]", b);
            }

            for (int i = 1; i < len; i++) {
                Utility.Read(stream, B, i, 1);
            }

            return utf_8.GetString(B, 0, len)[0];
        }
        static Encoding utf_8 = System.Text.Encoding.UTF8;
    }
}
